#include "sm2_operation.h"
#include "sm2_format.h"
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>

#define OP_ADD 0
#define OP_SUB 1
#define OP_NEG 2
#define OP_MUL 3
#define OP_SQR 4
#define OP_EXP 5
#define OP_INV 6

#define OP_DBL 11
#define OP_TRI 12
#define OP_HAF 13

void print_perimeter()
{
    int i = 0;
    for(i = 0; i < 15; ++i) {
        printf("=");
    }
    printf("\n");
}


struct {
		char *label;
		int op;
		char *r;
		char *a;
		char *b;
	} tests_modp[] = {
		{
		"x + y (mod p)", OP_ADD,
		"eefbe4cf140ff8b5b956d329d5a2eae8608c933cb89053217439786e54866567",
		"32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7",
		"bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0",
		},
		{
		"2*y (mod p)", OP_DBL,
		"786e6d46e9ecef38b37b9dc6d6d242a7a1530efa8c548e7f05be65ca4273e141",
		"bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0",
		NULL,
		},
		{
		"3*y (mod p)", OP_TRI,
		"34a5a3eadee366d50d396caa423b63fb71fc9678527ed5be089d98af63add1e2",
		"bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0",
		NULL,
		},
		{
		"x - y (mod p)", OP_SUB,
		"768d77882a23097d05db3562fed0a840bf3984422c3bc4a26e7b12a412128426",
		"32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7",
		"bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0",
		},
		{
		"y - x (mod p)", OP_SUB,
		"89728876d5dcf682fa24ca9d012f57bf40c67bbcd3c43b5e9184ed5beded7bd9",
		"bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0",
		"32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7",
		},
		{
		"-x (mod p)", OP_NEG,
		"cd3b51d2e0e67ee6a066fbb995c6366b701cf43f0d99f41f8ea5ba76ccb38b38",
		"32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7",
		NULL,
		},
		{
		"x/2 (mod p)", OP_HAF,
		"996257158f8cc08cafcc8223351ce4ca47f185df793305f138ad22c499a63a63",
		"32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7",
		NULL,
		},
		{
		"y/2 (mod p)", OP_HAF,
		"5e1b9b517a7b3bce2cdee771b5b490a9e854c3be631523a0016f9972909cf850",
		"bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0",
		NULL,
		},
		{
		"x * y (mod p)", OP_MUL,
		"edd7e745bdc4630ccfa1da1057033a525346dbf202f082f3c431349991ace76a",
		"32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7",
		"bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0",
		},
		{
		"x^2 (mod p)", OP_SQR,
		"f4e2cca0bcfd67fba8531eebff519e4cb3d47f9fe8c5eff5151f4c497ec99fbf",
		"32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7",
		NULL,
		},
		{
		"x^y (mod p)", OP_EXP,
		"8cafd11b1a0d2072b82911ba87e0d376103a1be5986fce91d8d297b758f68146",
		"32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7",
		"bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0",
		},
		{
		"x^-1 (mod p)", OP_INV,
		"053b878fb82e213c17e554b9a574b7bd31775222704b7fd9c7d6f8441026cd80",
		"32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7",
		NULL,
		},
	};
int modp_test(void)
{
    sm2_uint256_t result = {0};
    sm2_uint256_t _a = {0}, _b = {0};
    char *calc_result;
    for(int i = 0; i < 8; ++i)
    {
        sm2_set_zero(result);
        if(tests_modp[i].a != NULL) char_to_hex(_a, tests_modp[i].a);
        if(tests_modp[i].b != NULL) char_to_hex(_b, tests_modp[i].b);
        printf("label: %s\n", tests_modp[i].label);
        switch(tests_modp[i].op){
            case OP_ADD:
                sm2_mod_add(result, _a, _b);
                printf("计算结果: %s\n", hex_to_char(result));
                printf("实际结果: %s\n", tests_modp[i].r);
                if(!strcmp(tests_modp[i].r, hex_to_char(result))) printf("结果一致!\n");
                else printf("结果错误\n");
                print_perimeter();
                break;
            case OP_MUL:
                sm2_mod_mul(result, _a, _b);
                printf("计算结果: %s\n", hex_to_char(result));
                printf("实际结果: %s\n", tests_modp[i].r);
                if(!strcmp(tests_modp[i].r, hex_to_char(result))) printf("结果一致!\n");
                else printf("结果错误\n");
                print_perimeter();
                break;
            case OP_SUB:
                sm2_mod_sub(result, _a, _b);
                printf("计算结果: %s\n", hex_to_char(result));
                printf("实际结果: %s\n", tests_modp[i].r);
                if(!strcmp(tests_modp[i].r, hex_to_char(result))) printf("结果一致!\n");
                else printf("结果错误\n");
                print_perimeter();
                break;
            case OP_NEG:
                sm2_mod_neg(result, _a);
                printf("计算结果: %s\n", hex_to_char(result));
                printf("实际结果: %s\n", tests_modp[i].r);
                if(!strcmp(tests_modp[i].r, hex_to_char(result))) printf("结果一致!\n");
                else printf("结果错误\n");
                print_perimeter();
                break;
            case OP_SQR:
                sm2_mod_sqr(result, _a);
                printf("计算结果: %s\n", hex_to_char(result));
                printf("实际结果: %s\n", tests_modp[i].r);
                if(!strcmp(tests_modp[i].r, hex_to_char(result))) printf("结果一致!\n");
                else printf("结果错误\n");
                print_perimeter();
                break;
            // case OP_EXP:
            //     sm2_mod_exp(result, _a, _b);
            //     printf("计算结果: %s\n", hex_to_char(result));
            //     printf("实际结果: %s\n", tests_modp[i].r);
            //     if(!strcmp(tests_modp[i].r, hex_to_char(result))) printf("结果一致!\n");
            //     else printf("结果错误\n");
            //     print_perimeter();
            //     break;
            case OP_INV:
                sm2_fast_mod_inverse(result, _a);
                printf("计算结果: %s\n", hex_to_char(result));
                printf("实际结果: %s\n", tests_modp[i].r);
                if(!strcmp(tests_modp[i].r, hex_to_char(result))) printf("结果一致!\n");
                else printf("结果错误\n");
                print_perimeter();
                break;
        }
    }
}

struct {
		char *label;
        uint32_t op;
		char *r;
		char *a;
		char *b;
	} tests[] = {
		{
		"x + y (mod n)", OP_ADD,
		"eefbe4cf140ff8b5b956d329d5a2eae8608c933cb89053217439786e54866567",
		"32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7",
		"bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0",
		},
		{
		"x - y (mod n)", OP_SUB,
		"768d77882a23097d05db3562fed0a840313d63ae4e01c9ccc23706ad4be7c54a",
		"32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7",
		"bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0",
		},
		{
		"y - x (mod n)", OP_SUB,
		"89728876d5dcf682fa24ca9d012f57bf40c67bbcd3c43b5e9184ed5beded7bd9",
		"bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0",
		"32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7",
		},
		{
		"-x (mod n)", OP_NEG,
		"cd3b51d2e0e67ee6a066fbb995c6366ae220d3ab2f5ff949e261ae800688cc5c",
		"32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7",
		NULL,
		},
		{
		"x * y (mod n)", OP_MUL,
		"cf7296d5cbf0b64bb5e9a11b294962e9c779b41c038e9c8d815234a0df9d6623",
		"32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7",
		"bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0",
		},
		{
		"x^2 (mod n)", OP_SQR,
		"82d3d1b296d3a3803888b7ffc78f23eca824e7ec8d7ddaf231ffb0d256a19da2",
		"32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7",
		NULL,
		},
		{
		"x^y (mod n)", OP_EXP,
		"0cf4df7e76d7d49ff23b94853a98aba1e36e9ca0358acbf23a3bbda406f46df3",
		"32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7",
		"bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0",
		},
		{
		"x^-1 (mod n)", OP_INV,
		"96340ec8b80f44e9b345a706bdb5c9e3ab8a6474a5cb4e0d4645dbaecf1cf03d",
		"32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7",
		NULL,
		},
	};

// int main(void)
// {
//     sm2_uint256_t n1 = {0}, n2 = {0};
//     sm2_uint256_t result = {0};
//     // char *c1 = "0000000000000000000000000000000000000000000000000000000000000002";
//     // char *c2 = "0000000000000000000000000000000000000000000000000000000000000001";

//     char *c1 = "32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7";
//     char *c2 = "bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0";
    
//     char_to_hex(n1, c1);
//     char_to_hex(n2, c2);

//     _print_256(n1);
//     _print_256(n2);

//     sm2_modn_add(result, n1, n2);
//     _print_256(result);
    
//     printf("%s", hex_to_char(result));
// }

int modn_test(void)
{
    sm2_uint256_t result = {0};
    sm2_uint256_t _a = {0}, _b = {0};
    char *calc_result;
    for(int i = 0; i < 8; ++i)
    {
        sm2_set_zero(result);
        if(tests[i].a != NULL) char_to_hex(_a, tests[i].a);
        if(tests[i].b != NULL) char_to_hex(_b, tests[i].b);
        printf("label: %s\n", tests[i].label);
        switch(tests[i].op){
            case OP_ADD:
                sm2_modn_add(result, _a, _b);
                printf("计算结果: %s\n", hex_to_char(result));
                printf("实际结果: %s\n", tests[i].r);
                if(!strcmp(tests[i].r, hex_to_char(result))) printf("结果一致!\n");
                else printf("结果错误\n");
                print_perimeter();
                break;
            case OP_MUL:
                sm2_modn_mul(result, _a, _b);
                printf("计算结果: %s\n", hex_to_char(result));
                printf("实际结果: %s\n", tests[i].r);
                if(!strcmp(tests[i].r, hex_to_char(result))) printf("结果一致!\n");
                else printf("结果错误\n");
                print_perimeter();
                break;
            case OP_SUB:
                sm2_modn_sub(result, _a, _b);
                printf("计算结果: %s\n", hex_to_char(result));
                printf("实际结果: %s\n", tests[i].r);
                if(!strcmp(tests[i].r, hex_to_char(result))) printf("结果一致!\n");
                else printf("结果错误\n");
                print_perimeter();
                break;
            case OP_NEG:
                sm2_modn_neg(result, _a);
                printf("计算结果: %s\n", hex_to_char(result));
                printf("实际结果: %s\n", tests[i].r);
                if(!strcmp(tests[i].r, hex_to_char(result))) printf("结果一致!\n");
                else printf("结果错误\n");
                print_perimeter();
                break;
            case OP_SQR:
                sm2_modn_sqr(result, _a);
                printf("计算结果: %s\n", hex_to_char(result));
                printf("实际结果: %s\n", tests[i].r);
                if(!strcmp(tests[i].r, hex_to_char(result))) printf("结果一致!\n");
                else printf("结果错误\n");
                print_perimeter();
                break;
            case OP_EXP:
                sm2_modn_exp(result, _a, _b);
                printf("计算结果: %s\n", hex_to_char(result));
                printf("实际结果: %s\n", tests[i].r);
                if(!strcmp(tests[i].r, hex_to_char(result))) printf("结果一致!\n");
                else printf("结果错误\n");
                print_perimeter();
                break;
            case OP_INV:
                sm2_modn_inverse(result, _a);
                printf("计算结果: %s\n", hex_to_char(result));
                printf("实际结果: %s\n", tests[i].r);
                if(!strcmp(tests[i].r, hex_to_char(result))) printf("结果一致!\n");
                else printf("结果错误\n");
                print_perimeter();
                break;
        }
    }
}

int main(void)
{
    modp_test();
    return 0;
}